using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using System.Configuration;

namespace AspWebUtilities.Farm
{
	public partial class FarmData
	{
		class SectionTag
		{
			public const String FRAM_DATA_SECTION = "FarmData";
			public const String FRAM_DATA = "FarmData";
			public const String DB_CONNECTION_STRING = "DbConnectionString";
			public const String APPLICATION_ID = "ApplicationId";
			public const String TIME_TO_WAIT = "LockWaitTime";
		}

		class StoredProcedure
		{
			public const String LOAD_DATA				= "dbo.LoadData";
            public const String STORE_DATA              = "dbo.StoreData";
            public const String REMOVE_DATA             = "dbo.RemoveData";
            public const String LOCK_DATA               = "dbo.LockData";
            public const String UNLOCK_DATA             = "dbo.UnlockData";
            public const String UNLOCK_ALL_OWNE_DATA    = "dbo.UnlockAllOwneData";
            public const String UNLOCK_ALL_DATA         = "dbo.UnlockAllData";
			public const String VERSION					= "dbo.LastUpdate";
			public const String IS_LOCKED				= "dbo.IsLocked";
		}

		class ParameterName
		{
			public const String APPLICATION_ID		    = "@ApplicationID";
			public const String DATA_NAME				= "@DataName";
			public const String CLASS_NAME				= "@ClassName";
			public const String DATA_CONTENT			= "@DataContent";
			public const String SERVER_DNS_NAME			= "@ServerDnsName";
			public const String LOCKED_BY_SERVER		= "@LockedByServer";
			public const String LAST_UPDATE				= "@LastUpdate";
			public const String RETURN					= "@Return";
			public const String IS_LOCKED				= "@IsLocked";
			public const String LOCK_WAIT_TIME			= "@LockWaitTime";
		}

		static SqlConnection DbConnection = null;
		static SqlCommand DbCommand = null;

		static Guid ApplicationId
		{
			get
			{
				String ss = ((System.Collections.Hashtable)ConfigurationManager.GetSection(SectionTag.FRAM_DATA_SECTION))[SectionTag.APPLICATION_ID].ToString();
				if(ss == "")
					throw new Exception("No Application ID");
				return new Guid(ss);
			}
		}

		static int LockWaitTime
		{
			get
			{
				String ss = ((System.Collections.Hashtable)ConfigurationManager.GetSection(SectionTag.FRAM_DATA_SECTION))[SectionTag.TIME_TO_WAIT].ToString();
				if (ss == "")
					return -1;
				return Convert.ToInt32(ss);
			}
		}


		static void PrepareCommand()
		{
			if (DbConnection == null)
			{
				String cs = ((System.Collections.Hashtable)ConfigurationManager.GetSection(SectionTag.FRAM_DATA_SECTION))[SectionTag.DB_CONNECTION_STRING].ToString();

				DbConnection = new SqlConnection(cs);
			}
			if (DbConnection.State != System.Data.ConnectionState.Open)
			{
				DbConnection.Open();
			}

			DbCommand = null;
			DbCommand = new SqlCommand();
			DbCommand.CommandType = System.Data.CommandType.StoredProcedure;
			DbCommand.Connection = DbConnection;
		}

		static void LoadData(String DataName, ref Object oo, ref Boolean isLockedByMe, ref UInt64 lastU)
		{
			PrepareCommand();
			DbCommand.CommandText = StoredProcedure.LOAD_DATA;
			SqlParameter Param;

			//ApplicationID
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.APPLICATION_ID;
			Param.DbType = System.Data.DbType.Guid;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = ApplicationId;
			DbCommand.Parameters.Add(Param);

			//DataName
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.DATA_NAME;
			Param.DbType = System.Data.DbType.String;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = DataName;
			DbCommand.Parameters.Add(Param);

			//ClassName
			Param = DbCommand.CreateParameter();
			DbCommand.Parameters.Add(Param);
			Param.ParameterName = ParameterName.CLASS_NAME;
			Param.DbType = System.Data.DbType.String;
			Param.Direction = System.Data.ParameterDirection.Input;
			if (oo != null)
				Param.Value = oo.GetType().FullName;
			else
				Param.Value = DBNull.Value;

			//DataContent
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.DATA_CONTENT;
			Param.DbType = System.Data.DbType.Binary;
			Param.Size = int.MaxValue;
			Param.Direction = System.Data.ParameterDirection.Output;
			DbCommand.Parameters.Add(Param);

			//ServerName
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.SERVER_DNS_NAME;
			Param.DbType = System.Data.DbType.String;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = System.Net.Dns.GetHostName();
			DbCommand.Parameters.Add(Param);

			//LockedByMe
			Param = new SqlParameter(ParameterName.LOCKED_BY_SERVER, System.Data.SqlDbType.Bit);
			Param.Direction = System.Data.ParameterDirection.Output;
			DbCommand.Parameters.Add(Param);

			//LastUpdate
			Param = new SqlParameter(ParameterName.LAST_UPDATE, System.Data.SqlDbType.Timestamp);
			Param.Direction = System.Data.ParameterDirection.Output;
			DbCommand.Parameters.Add(Param);

			//Return Value
			Param = new SqlParameter(ParameterName.RETURN, System.Data.SqlDbType.Int);
			Param.Direction = System.Data.ParameterDirection.ReturnValue;
			DbCommand.Parameters.Add(Param);

			DbCommand.ExecuteNonQuery();

			Param = DbCommand.Parameters[ParameterName.RETURN];

			int res = (int)Param.Value;

			ThrowException(res);

			Param = DbCommand.Parameters[ParameterName.DATA_CONTENT];
			oo = ToObject((byte[])Param.Value);

			Param = DbCommand.Parameters[ParameterName.LOCKED_BY_SERVER];
			isLockedByMe = (Boolean)Param.Value;

			Param = DbCommand.Parameters[ParameterName.LAST_UPDATE];
			lastU = ToNumber((Byte[])Param.Value);
		}

		static void SaveData(String DataName, Object oo, ref UInt64 lastU,ref bool IsLockedByMe)
		{
			PrepareCommand();

			DbCommand.CommandText = StoredProcedure.STORE_DATA;
			SqlParameter Param;
			
			//ApplicationID
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.APPLICATION_ID;
			Param.DbType = System.Data.DbType.Guid;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = ApplicationId;
			DbCommand.Parameters.Add(Param);

			//DataName
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.DATA_NAME;
			Param.DbType = System.Data.DbType.String;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = DataName;
			DbCommand.Parameters.Add(Param);

			//ClassName
			Param = DbCommand.CreateParameter();
			DbCommand.Parameters.Add(Param);
			Param.ParameterName = ParameterName.CLASS_NAME;
			Param.DbType = System.Data.DbType.String;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = oo.GetType().FullName;

			//DataContent
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.DATA_CONTENT;
			Param.DbType = System.Data.DbType.Binary;
			Param.Size = int.MaxValue;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = ToBinary(oo);
			DbCommand.Parameters.Add(Param);

			//ServerName
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.SERVER_DNS_NAME;
			Param.DbType = System.Data.DbType.String;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = System.Net.Dns.GetHostName();
			DbCommand.Parameters.Add(Param);

			//IsLocked
			Param = new SqlParameter(ParameterName.IS_LOCKED, System.Data.SqlDbType.Bit);
			Param.Direction = System.Data.ParameterDirection.Output;
			DbCommand.Parameters.Add(Param);			
			
			//LastUpdate
			Param = new SqlParameter(ParameterName.LAST_UPDATE,System.Data.SqlDbType.Timestamp);
			Param.Direction = System.Data.ParameterDirection.Output;
			DbCommand.Parameters.Add(Param);
		
			//Return Value
			Param = new SqlParameter(ParameterName.RETURN, System.Data.SqlDbType.Int);
			Param.Direction = System.Data.ParameterDirection.ReturnValue;
			DbCommand.Parameters.Add(Param);			

			DbCommand.ExecuteNonQuery();

			Param = DbCommand.Parameters[ParameterName.RETURN];

			int res = (int)Param.Value;

			ThrowException(res);

			Param = DbCommand.Parameters[ParameterName.IS_LOCKED];
			IsLockedByMe = (Boolean)Param.Value;

			Param = DbCommand.Parameters[ParameterName.LAST_UPDATE];
			lastU = ToNumber((Byte[])Param.Value);
		}

		static Boolean RemoveData(String DataName)
		{
			PrepareCommand();

			DbCommand.CommandText = StoredProcedure.REMOVE_DATA;
			SqlParameter Param;

			//ApplicationID
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.APPLICATION_ID;
			Param.DbType = System.Data.DbType.Guid;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = ApplicationId;
			DbCommand.Parameters.Add(Param);

			//DataName
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.DATA_NAME;
			Param.DbType = System.Data.DbType.String;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = DataName;
			DbCommand.Parameters.Add(Param);

			//ServerName
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.SERVER_DNS_NAME;
			Param.DbType = System.Data.DbType.String;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = System.Net.Dns.GetHostName();
			DbCommand.Parameters.Add(Param);

			//Return Value
			Param = new SqlParameter(ParameterName.RETURN, System.Data.SqlDbType.Int);
			Param.Direction = System.Data.ParameterDirection.ReturnValue;
			DbCommand.Parameters.Add(Param);

			DbCommand.ExecuteNonQuery();

			Param = DbCommand.Parameters[ParameterName.RETURN];

			int res = (int)Param.Value;

			return ThrowException(res); // OK
		}

		static Boolean LockData(String DataName)
		{
			PrepareCommand();


			DbCommand.CommandText = StoredProcedure.LOCK_DATA;
			SqlParameter Param;

			//ApplicationID
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.APPLICATION_ID;
			Param.DbType = System.Data.DbType.Guid;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = ApplicationId;
			DbCommand.Parameters.Add(Param);

			//DataName
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.DATA_NAME;
			Param.DbType = System.Data.DbType.String;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = DataName;
			DbCommand.Parameters.Add(Param);

			if (LockWaitTime != -1)
			{
				//LockWaitTime
				Param = DbCommand.CreateParameter();
				Param.ParameterName = ParameterName.LOCK_WAIT_TIME;
				Param.DbType = System.Data.DbType.Int32;
				Param.Direction = System.Data.ParameterDirection.Input;
				Param.Value = LockWaitTime;
				DbCommand.Parameters.Add(Param);
			}

			//ServerName
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.SERVER_DNS_NAME;
			Param.DbType = System.Data.DbType.String;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = System.Net.Dns.GetHostName();
			DbCommand.Parameters.Add(Param);

			//Return Value
			Param = new SqlParameter(ParameterName.RETURN, System.Data.SqlDbType.Int);
			Param.Direction = System.Data.ParameterDirection.ReturnValue;
			DbCommand.Parameters.Add(Param);

			DbCommand.ExecuteNonQuery();

			Param = DbCommand.Parameters[ParameterName.RETURN];

			int res = (int)Param.Value;

			return ThrowException(res); // OK
		}

		static Boolean UnLockData(String DataName)
		{
			PrepareCommand();

			DbCommand.CommandText = StoredProcedure.UNLOCK_DATA;
			SqlParameter Param;

			//ApplicationID
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.APPLICATION_ID;
			Param.DbType = System.Data.DbType.Guid;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = ApplicationId;
			DbCommand.Parameters.Add(Param);

			//DataName
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.DATA_NAME;
			Param.DbType = System.Data.DbType.String;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = DataName;
			DbCommand.Parameters.Add(Param);

			//ServerName
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.SERVER_DNS_NAME;
			Param.DbType = System.Data.DbType.String;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = System.Net.Dns.GetHostName();
			DbCommand.Parameters.Add(Param);

			//Return Value
			Param = new SqlParameter(ParameterName.RETURN, System.Data.SqlDbType.Int);
			Param.Direction = System.Data.ParameterDirection.ReturnValue;
			DbCommand.Parameters.Add(Param);

			DbCommand.ExecuteNonQuery();

			Param = DbCommand.Parameters[ParameterName.RETURN];

			int res = (int)Param.Value;

			return ThrowException(res); // OK
		}

		static Boolean UnLockAllOwneDatas()
		{
			PrepareCommand();

			DbCommand.CommandText = StoredProcedure.UNLOCK_ALL_OWNE_DATA;
			SqlParameter Param;

			//ApplicationID
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.APPLICATION_ID;
			Param.DbType = System.Data.DbType.Guid;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = ApplicationId;
			DbCommand.Parameters.Add(Param);

			//ServerName
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.SERVER_DNS_NAME;
			Param.DbType = System.Data.DbType.String;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = System.Net.Dns.GetHostName();
			DbCommand.Parameters.Add(Param);

			//Return Value
			Param = new SqlParameter(ParameterName.RETURN, System.Data.SqlDbType.Int);
			Param.Direction = System.Data.ParameterDirection.ReturnValue;
			DbCommand.Parameters.Add(Param);

			DbCommand.ExecuteNonQuery();

			Param = DbCommand.Parameters[ParameterName.RETURN];

			int res = (int)Param.Value;

			return ThrowException(res); // OK
		}

		static Boolean UnLockAllDatas()
		{
			PrepareCommand();

			DbCommand.CommandText = StoredProcedure.UNLOCK_ALL_DATA;
			SqlParameter Param;

			//ApplicationID
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.APPLICATION_ID;
			Param.DbType = System.Data.DbType.Guid;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = ApplicationId;
			DbCommand.Parameters.Add(Param);

			//Return Value
			Param = new SqlParameter(ParameterName.RETURN, System.Data.SqlDbType.Int);
			Param.Direction = System.Data.ParameterDirection.ReturnValue;
			DbCommand.Parameters.Add(Param);

			DbCommand.ExecuteNonQuery();

			Param = DbCommand.Parameters[ParameterName.RETURN];

			int res = (int)Param.Value;

			return ThrowException(res); // OK
		}

		static UInt64 LastUpdate(String DataName)
		{
			PrepareCommand();
			DbCommand.CommandText = StoredProcedure.VERSION;
			SqlParameter Param;

			//ApplicationID
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.APPLICATION_ID;
			Param.DbType = System.Data.DbType.Guid;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = ApplicationId;
			DbCommand.Parameters.Add(Param);

			//DataName
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.DATA_NAME;
			Param.DbType = System.Data.DbType.String;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = DataName;
			DbCommand.Parameters.Add(Param);


			//LastUpdate
			Param = new SqlParameter(ParameterName.LAST_UPDATE, System.Data.SqlDbType.Timestamp);
			Param.Direction = System.Data.ParameterDirection.Output;
			DbCommand.Parameters.Add(Param);

			//Return Value
			Param = new SqlParameter(ParameterName.RETURN, System.Data.SqlDbType.Int);
			Param.Direction = System.Data.ParameterDirection.ReturnValue;
			DbCommand.Parameters.Add(Param);

			DbCommand.ExecuteNonQuery();

			Param = DbCommand.Parameters[ParameterName.RETURN];

			int res = (int)Param.Value;

			ThrowException(res);

			Param = DbCommand.Parameters[ParameterName.LAST_UPDATE];

			return ToNumber((Byte[])Param.Value); // TimeStamp
		}

		static bool IsLocked(String DataName,ref String ServerDnsName)
		{
			PrepareCommand();

			PrepareCommand();
			DbCommand.CommandText = StoredProcedure.IS_LOCKED;
			SqlParameter Param;

			//ApplicationID
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.APPLICATION_ID;
			Param.DbType = System.Data.DbType.Guid;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = ApplicationId;
			DbCommand.Parameters.Add(Param);

			//DataName
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.DATA_NAME;
			Param.DbType = System.Data.DbType.String;
			Param.Direction = System.Data.ParameterDirection.Input;
			Param.Value = DataName;
			DbCommand.Parameters.Add(Param);

			//ServerName
			Param = DbCommand.CreateParameter();
			Param.ParameterName = ParameterName.SERVER_DNS_NAME;
			Param.DbType = System.Data.DbType.String;
			Param.Direction = System.Data.ParameterDirection.Output;
			Param.Value = System.Net.Dns.GetHostName();
			DbCommand.Parameters.Add(Param);


			//IsLocked
			Param = new SqlParameter(ParameterName.IS_LOCKED, System.Data.SqlDbType.Bit);
			Param.Direction = System.Data.ParameterDirection.Output;
			DbCommand.Parameters.Add(Param);

			//Return Value
			Param = new SqlParameter(ParameterName.RETURN, System.Data.SqlDbType.Int);
			Param.Direction = System.Data.ParameterDirection.ReturnValue;
			DbCommand.Parameters.Add(Param);

			DbCommand.ExecuteNonQuery();

			Param = DbCommand.Parameters[ParameterName.RETURN];

			int res = (int)Param.Value;

			ThrowException(res);

			Param = DbCommand.Parameters[ParameterName.SERVER_DNS_NAME];
			ServerDnsName = Param.Value as String;

			Param = DbCommand.Parameters[ParameterName.IS_LOCKED];

			return (Boolean)Param.Value;

		}

		static bool ThrowException(int resCode)
		{
			switch (resCode)
			{
				case 1: throw new LockedDataException();
					break;
				case 2: throw new NotSameClassTypeException();
					break;
				case 3: throw new DataDoesntExistsException();
					break;
				default:
				case 0:
					return true;
			}
			return false;
		}
	}
}
